#include "mm_public.h"

/* Returns microseconds. */
double comp_time (struct timeval times, struct timeval timee)
{
	double elap = 0.0;

	if (timee.tv_sec > times.tv_sec) {
		elap += (((double)(timee.tv_sec - times.tv_sec -1))*1000000.0);
		elap += timee.tv_usec + (1000000-times.tv_usec);
	}
	else {
		elap = timee.tv_usec - times.tv_usec;
	}
	return ((unsigned long)(elap));
}

void rearrange(mm_t *MM,node* curNode,int get)
{		
	node *tmp = MM->free_list;
	int check = MM->max_avail_size; //Used to see if the max available size has changed
	int check2 = 0; //Used as temporary high counter for get/put calls
	int test = 0;
	
	if(get == 1) //This is only called by the mm_get function to check if max available size has changed
	{
		while(tmp != NULL)
		{
			
			if(tmp->flag == 0) //Signifies node is free
			{
				if(tmp->size > check2)
				{
					check2 = tmp->size; //This will find the highest size available in the space allocated
				}
			}
			
			tmp = tmp->next;
		}
		if(check2 != check)
		{
			MM->max_avail_size = check2;
		}
		return;
	}
	
	
	else //Only the put function call will get here 
	{
		node *tmp3;
		node *tmp2 = curNode;
		
		if(tmp2->next == NULL)
		{
			test = 1;
		}
		if(tmp2->next->flag == 0 && test == 0) //See if the next partition is free, or exists
		{
			tmp3 = tmp2->next;
			tmp2->size += tmp3->size;
			tmp2->next = tmp3->next;
			check2 = tmp2->size;
			free(tmp3); //This node should no longer exist
		}
		
		if(tmp2->prev->flag == 0) //Checks to see if previous node is free
		{
			tmp3 = tmp2->prev;
			tmp3->size += tmp2->size;
			tmp3->next = tmp2->next;
			check2 = tmp3->size;
			free(tmp2); //Will not need this node
		}
		
		if(check2 > check) //If newly created structure has a greater size than the max available size
		{
			MM->max_avail_size = check2;
		}
		
		return;
	}
	
}

/* Write these ... */
int mm_init (mm_t *MM, int tsz)
{
	static int initCalled = 0; //Used to check if init has already been called

	if(initCalled == 1)
	{
		perror("Called Init more than once, not allowed");
		return -1;
	}

	initCalled = 1; //Used above saying init has already been called
	
	
	MM->tsz = tsz;
	MM->partitions = 1; //Since this 1st node will carry the entire size
	MM->max_avail_size = tsz;
	
	MM->free_list = (node*) malloc(sizeof(node)); //Head node, will contain no address used as head node only
	node *tmp2 = (node*) malloc(sizeof(node)); //Will carry the size of the initial
	
	tmp2->prev = MM->free_list;
	tmp2->next = NULL;
	tmp2->flag = 0;//Representing Free
	tmp2->address = (void*) malloc(tsz);
	tmp2->size = tsz;
	
	MM->free_list->next = tmp2;
	MM->free_list->prev = NULL; //Should be only node with NULL representing header node
	MM->free_list->size = 0; //Header node does not contain any size or address
	MM->free_list->flag = 2; //2 will represent the Header node

	return 0; //Represents success
}

void* mm_get (mm_t *MM, int neededSize) 
{
	node *tmp;
	node *final; //Used as the final return of this function
	tmp = MM->free_list;

	if(MM->max_avail_size < neededSize)
	{
		perror("Not enough Room");
		return;
	}
	
	while(tmp != NULL) //To iterate through the lists
	{
		if((tmp->size >= neededSize) && (tmp->flag == 0)) //Found Space with enough room to allocate for user, and free for the user to use
		{
			if(tmp->size == neededSize)
			{
				tmp->flag = 1; //Used to show that this address is taken
				final = tmp;
				break;
			}

			else
			{ 
				node *tmp2 = (node*) malloc(sizeof(node)); //To create a new node
				tmp2->size = tmp->size - neededSize;
				tmp2->flag = 0; //Used to show that this address is taken
				tmp2->prev = tmp;
				tmp2->next = tmp->next;
				tmp2->address = tmp->address + neededSize;
				
				tmp->next = tmp2;
				tmp->size = neededSize;

				final = tmp;
				break;
			}
		}
		tmp = tmp->next;
	}

	//Call function with 1 to see if max_avail_size has changed
	rearrange(MM, final,1); //only thing that matters here is MM and the int 1, final is simply used to fill the required inputs
	
	return final->address;
}

void mm_put (mm_t *MM, void *chunk) 
{
	node *tmp;
	tmp = MM->free_list;
	while(tmp != NULL)
	{
		if(tmp->address == chunk)
		{
			tmp->flag = 0; //To give this back to the user, "free"
			break;
		}
		tmp = tmp->next;
	}
	rearrange(MM,tmp, 0); //Here tmp will be used to check if there is free space on either side of the node that is put back
	return;
}

void mm_release (mm_t *MM) 
{
	node *tmp;
	node *tmp2;
	tmp = MM->free_list;
	
	while(tmp->next != NULL)
	{
		tmp2 = tmp;  //Sets tmp2 equal to the current tmp node, starts as header node
		tmp = tmp->next; //Sets tmp equal to the next node
		if(tmp2->flag == 2)
		{
			free(tmp2->next->address); //Actual header node doesn't carry size but the next node has the actual address of tsz then everything else is freed
		}
		free(tmp2); //frees the tmp2 node begins as header node
	}

	free(tmp); //frees the last node of the list

}
